﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Dwarf.Managed
{
    /// <summary>
    /// Solve the Unstructured Grid problem with TPL parallelization  
    /// </summary>
    /// <remarks> 
    /// For the demonstration purposes of the project, we have chosen two-dimensional iterative triangle grid. 
    /// Function used on each vertex of the grid is arithmetic mean of the neighbors’ vertices. 
    /// The test of convergence performed is the comparison of difference of the same vertices on latest iterations to epsilon.
    /// </remarks>
    class Solver
    {
        /// <summary>
        /// Power of graph.
        /// </summary>
        public int numVertex;
        /// <summary>
        /// Number of triangles in grid.
        /// </summary>
        public int numTriangles;

        /// <summary>
        /// Epsilon.
        /// </summary>
        public double epsilon;

        /// <summary>
        /// Values of verteces.
        /// </summary>
        public double[] value;
        /// <summary>
        /// Temp array.
        /// </summary>
        public double[] tempValue;
        /// <summary>
        /// Result values.
        /// </summary>
        public double[] resultValue;

        /// <summary>
        /// Array for neighbors.
        /// </summary>
        public int[][] neighborhood;
        /// <summary>
        /// Verteces of triangles.
        /// </summary>
        public int[][] triangles;

        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Solver()
        {
        }

        /// <summary>
        /// Arithmetic mean of the neighbors’ vertices.
        /// </summary>
        private double compute(int[] neighbors, double[] input)
        {
            double output = 0.0;
            for (int i = 0; i < neighbors.Length; i++)
            {
                output += input[neighbors[i]];
            }
            return output / neighbors.Length;
        }

        /// <summary>
        /// TPL based method.
        /// </summary>
        public void Solve()
        {
            bool converged = false;                 //Flag of convergance

            //Loop while the test of convergence for each vertex is not passed
            while (!converged) 
            {
                converged = true;
                
                // Loop for number of vertices.
                //Since the iterations of the loop are independent by data, 
                //each iteration may be performed as separate thread.
                Parallel.For(0, numVertex, (i) =>
                {                    
                    tempValue[i] = compute(neighborhood[i], value);

                    //Check test of convergence for current vertex.
                    if (converged && (Math.Abs(tempValue[i] - value[i]) >= epsilon)) converged = false;
                });
                double[] t = tempValue;
                tempValue = value;
                value = t;
            }

            resultValue = value;
        }
    }
}